package com.un.ebs.sys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.un.ebs.core.TenantInfo;
import com.un.ebs.core.UserContext;
import com.un.ebs.core.enums.SexEnum;
import com.un.ebs.core.enums.UserTypeEnum;
import com.un.ebs.core.exception.*;
import com.un.ebs.core.util.*;
import com.un.ebs.customer.domain.Customer;
import com.un.ebs.customer.service.CustomerService;
import com.un.ebs.sys.SysConfigureCode;
import com.un.ebs.sys.domain.*;
import com.un.ebs.sys.dto.PowerDto;
import com.un.ebs.sys.dto.UserAddDto;
import com.un.ebs.sys.dto.UserDto;
import com.un.ebs.sys.dto.UserRegisterByWxDto;
import com.un.ebs.sys.mapper.*;
import com.un.ebs.sys.service.AppTenantService;
import com.un.ebs.sys.service.ConfigureService;
import com.un.ebs.sys.service.RedisService;
import com.un.ebs.sys.service.UserService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.impl.DefaultClaims;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author liubo
 * @since 2019-09-15
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final String user_key = "ebs_user_token:";

    @Resource
    private UserExtMapper userExtMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private EntDeptMapper entDeptMapper;

    @Resource
    private UserWxMapper userWxMapper;

    @Resource
    private CustomerService customerService;

    @Resource
    private AppTenantService appTenantService;

    @Resource
    private UserPowerExtMapper userPowerExtMapper;

    @Resource
    private AppPowerMapper appPowerMapper;

    @Resource
    private UserRoleExtMapper userRoleExtMapper;

    @Resource
    private AppUserRoleExtMapper appUserRoleExtMapper;

    @Resource
    private UserAppExtMapper userAppExtMapper;

    @Resource
    private AppTenantExtMapper appTenantExtMapper;

    @Resource
    private TenantAdminMapper tenantAdminMapper;

    @Resource
    private UserTenantAdminExtMapper userTenantAdminExtMapper;

    @Resource
    private AppUserRoleMapper appUserRoleMapper;

    @Resource
    private ConfigureService configureService;

    @Resource
    private PersonnelMapper personnelMapper;

    @Resource
    private UserWxExtMapper userWxExtMapper;

    @Autowired
    private RedisService redisService;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void add(UserAddDto req) {
        UserContext userContext = SecurityUtils.getUserContent();
        UserTypeEnum type;
        switch (userContext.getType()) {
            case ROOT:
                type = UserTypeEnum.DEVELOPER;
                break;
            case DEVELOPER:
            case USER:
            default:
                type = UserTypeEnum.USER;
        }
        add(req, type);
    }

    public void add(UserAddDto req, UserTypeEnum type) {
        User obj = new User();
        obj.setUserName(req.getUserName());
        obj.setPassword(AESTool.Encrypt(req.getPassword()));
        obj.setType(type);
        obj.setIsDelete(false);

        // 将当前用户的 tenantId 给新的用户
        if (!StringUtils.isNullOrEmpty(SecurityUtils.getUserContent().getTenantId())) {
            obj.setCurrentTenantId(SecurityUtils.getUserContent().getTenantId());
        }
        getBaseMapper().insert(obj);
    }

    @Override
    public User getUserByOpenId(String openId) {
        return getUserByOpenId(openId, null);
    }

    @Override
    public User getUserByOpenId(String openId, String tenantId) {
        LambdaQueryWrapper<UserWx> lq = Wrappers.lambdaQuery();
        lq.eq(UserWx::getOpenId, openId);
        if (!StringUtils.isNullOrEmpty(tenantId)) {
            lq.eq(UserWx::getTenantId, tenantId);
        }
        Optional<UserWx> opt = userWxMapper.selectList(lq).stream().findFirst();
        if (opt.isPresent()) {
            return getById(opt.get().getUserId());
        } else {
            return null;
        }
    }

    public void ENCYPwd() {
//        List<User>list=list();
//        for(User u : list){
//            u.setPassword(AESTool.Encrypt(u.getPassword()));
//            u.updateById();
//        }
    }

    @Override
    public User addByWx(String name, String phone, String openId, String handImage, String tenantId) {
        UserRegisterByWxDto obj = new UserRegisterByWxDto();
        obj.setName(name);
        obj.setPhone(phone);
        obj.setOpenId(openId);
        obj.setHandImage(handImage);
        obj.setTenantId(tenantId);
        return addByWx(obj);
    }

    @Override
    public User addByWx(String name, String openId, String handImage, String tenantId) {
        UserRegisterByWxDto obj = new UserRegisterByWxDto();
        obj.setName(name);
        obj.setPhone(null);
        obj.setOpenId(openId);
        obj.setHandImage(handImage);
        obj.setTenantId(tenantId);
        return addByWx(obj);
    }

    @Transactional
    @Override
    public void cancelWxBind(String openId, String tenantId) {
        LambdaQueryWrapper<UserWx> lq = Wrappers.lambdaQuery();
        lq.eq(UserWx::getOpenId, openId);
        lq.eq(UserWx::getTenantId, tenantId);
        Optional<UserWx> userWx = userWxMapper.selectList(lq).stream().findFirst();
        if (!userWx.isPresent()) {
            return;
        }
        if (!userWx.get().getUserId().equals(SecurityUtils.getUserContent().getId())) {
            throw new BusinessException("当前openId与要解除绑定的userId不匹配。");
        }
        if (!userWx.get().getTenantId().equals(SecurityUtils.getTenantId())) {
            throw new BusinessException("当前openId与要解除绑定的tenantId不匹配。");
        }

        userWx.get().deleteById();

        User user = baseMapper.selectById(userWx.get().getUserId());
        user.setOpenId(null);
        user.updateById();
    }

    @Transactional
    @Override
    public User addByWx(UserRegisterByWxDto req) {
        System.out.println("addByWx:");
        System.out.println("---------------------------------------------------");
        System.out.println(req);
        System.out.println();
        String userId = getUserId(req.getOpenId());
        User user = getById(userId);
        if (user == null) {
            user = queryByUserName(req.getOpenId());
            if (user == null) {
                user = queryByUserName(req.getPhone());
            }
            if (user == null) {
                logger.warn("没有找到用户（openId=" + req.getOpenId() + "），正在 Other 类型的用户创建。");
                user = new User();
                user.setUserName(StringUtils.isNullOrEmpty(req.getPhone()) ? req.getOpenId() : req.getPhone());
                user.setPassword(AESTool.Encrypt(req.getOpenId()));
                user.setType(UserTypeEnum.OTHER);
                user.setOpenId(req.getOpenId());
                if (StringUtils.isNullOrEmpty(req.getTenantId())) {
                    throw new BusinessException("当前租户ID不能为空。");
                } else {
                    user.setCurrentTenantId(req.getTenantId());
                }
            }
        } else {
            user.setOpenId(req.getOpenId());
            user.setCurrentTenantId(req.getTenantId());
        }
        user.insertOrUpdate();

        LambdaQueryWrapper<UserWx> lq = Wrappers.lambdaQuery();
        lq.eq(UserWx::getTenantId, req.getTenantId());
        lq.eq(UserWx::getOpenId, req.getOpenId());
        lq.orderByDesc(UserWx::getCreateTime);
        //lq.eq(UserWx::getUserId, user.getId());
        Optional<UserWx> userWxObj = userWxMapper.selectList(lq).stream().findFirst();

        UserWx userWx = null;
        if (userWxObj.isPresent()) {
            userWx = userWxObj.get();
        } else {
            //String urlKey = OSSUtil.upload(req.getHandImage(), OSSUtil.getPath("user", user.getId(), "logo"), ".jpg");
            userWx = new UserWx();
            userWx.setOpenId(req.getOpenId());
            userWx.setUserId(user.getId());
            //userWx.setHeadImage(urlKey);
            userWx.setHeadImage(req.getHandImage());
            userWx.setTenantId(req.getTenantId());
            userWx.insert();
        }
        userWx.setHeadImage(req.getHandImage());

//        LambdaQueryWrapper<Personnel> personnelQuery = Wrappers.lambdaQuery();
//        personnelQuery.eq(Personnel::getUserId,user.getId());
//        personnelQuery.eq(Personnel::getIsDelete,false);
//        Optional<Personnel> p = personnelMapper.selectList(personnelQuery).stream().findFirst();

        Customer cust = null;
        if (!StringUtils.isNullOrEmpty(req.getOpenId())) {
            cust = customerService.queryByUserId(user.getId());
        }
        if (!StringUtils.isNullOrEmpty(req.getPhone())) {
            cust = customerService.queryCustByMobile(req.getPhone(), req.getTenantId());
        }
        if (cust == null) {
            cust = customerService.queryByUserId(user.getId());
            if (cust == null) {
                cust = new Customer();
                cust.setName(req.getName());
                cust.setPhone(req.getPhone());
                cust.setWechatId(req.getOpenId());
                cust.setUserId(user.getId());
                cust.setCreateAt(user.getId());
                cust.setCreateTime(new Date());
                cust.setTenantId(req.getTenantId());
                cust.setIsDelete(false);
                cust.insert();
            } else {
                customerService.update(cust.getId(), req.getOpenId(), req.getName(), req.getPhone());
            }
        } else {
            customerService.update(cust.getId(), req.getOpenId(), req.getName(), req.getPhone());
        }
        return user;
    }

    @Transactional
    @Override
    public void setRole(String userId, List<String> roleIds) {
        if (!(SecurityUtils.getUserContent().getType() == UserTypeEnum.TENANT_ADMIN || SecurityUtils.getUserContent().getType() == UserTypeEnum.DEVELOPER)) {
            throw new BusinessPermissionDeniedException("你不是租户管理员。");
        }

        LambdaQueryWrapper<AppUserRole> lq = Wrappers.lambdaQuery();
        lq.eq(AppUserRole::getUserId, userId);
        lq.eq(AppUserRole::getTenantId, SecurityUtils.getTenantId());
        appUserRoleMapper.delete(lq);

        for (String id : roleIds) {
            AppUserRole appUserRole = new AppUserRole();
            appUserRole.setRoleId(id);
            appUserRole.setUserId(userId);
            appUserRole.setTenantId(SecurityUtils.getUserContent().getTenantId());
            appUserRole.insert();
        }
    }

    @Override
    public void changePwd(String oldPwd, String newPwd, String userId) {
        User obj = getById(userId);
        if (AESTool.Decrypt(obj.getPassword()).equals(oldPwd)) {
            obj.setPassword(AESTool.Encrypt(newPwd));
            obj.updateById();
        } else {
            throw new BusinessException("修改密码失败,原始密码不正确。");
        }
    }

    @Override
    public void delete(String id) {
        delete(id, true);
    }

    private void delete(String id, boolean isPowerCheck) {
        User user = getBaseMapper().selectById(id);
        if (user == null) {
            throw new BusinessException("用户没有找到。");
        }

//        if (isPowerCheck) {
//            UserContext userContext = SecurityUtils.getUserContent();
//            if(!(userContext.getType()==UserTypeEnum.DEVELOPER && user.getType()==UserTypeEnum.TENANT_ADMIN)){
//                throw new BusinessException("你没有权限执行此操作。");
//            }else{
//                //if (user.getType().getCode() > userContext.getType().getCode() && userContext.getType().getCode() < UserTypeEnum.USER.getCode()) {
//                throw new BusinessException("你没有权限执行此操作。");
//            }
//        }

        user.setUserName("`del-" + System.currentTimeMillis() + "`" + user.getUserName());
        user.setOpenId(null);
        user.updateById();
        user.deleteById();
    }

    @Override
    public void sendRegisterCode(String phone, String tenantId) {
        String key = String.format("%s-%s", phone, "value");
        String code = null;
        if (redisService.get(phone) != null) {
            code = redisService.get(key).toString();
        }
        //没有找到30秒内的Code，则重新生成并发送
        if (StringUtils.isNullOrEmpty(code)) {
            code = StringUtils.randomCode(6);

            //30秒可重发
            redisService.set(phone, code, 30L);
            //5分钟内有效
            redisService.set(key, code, 300L);

            System.out.println("------------------------------");
            System.out.println(phone + " : " + code);

            SmsUtils.sendRegisterSms(
                    phone,
                    code,
                    configureService.getValueByCode(SysConfigureCode.sms_verification_code_sign_name, tenantId),
                    configureService.getValueByCode(SysConfigureCode.sms_verification_code_templete_id, tenantId));

        } else {//否则提供用户不能频繁操作
            throw new BusinessException("30秒之后才能重新发送。");
        }

    }

    @Override
    public void sendRegisterCode(String phone) {
        sendRegisterCode(phone, SecurityUtils.getTenantId());
    }

    @Override
    public boolean checkRegisterCode(String phone, String code) {
        String key = String.format("%s-%s", phone, "value");
        if (code.equals(redisService.get(key).toString())) {
            return true;
        } else {
            logger.error(String.format("没有找到验证码：key=%s,code=%s", key, code));
            return false;
        }
    }

    @Transactional
    @Override
    public void bindUserToPersonnel(String openId, String phone, String tenantId) {

        // 找到手机号码对应的人员信息
        Personnel personnel = personnelMapper.queryByPhone(phone);
        if (personnel == null) {
            logger.error("你的手机号码【phone = " + phone + " , tenantId = " + tenantId + " , openId = " + openId + "】没有事先备案，不能绑定，请与管理员联系。");
            throw new BusinessException("你的手机号码" + phone + "没有备案，不能绑定，请与管理员联系。");
        }

        // 找到手机号码对应的用户信息
        User user = queryByUserName(phone);
        if (user == null) {
            if (StringUtils.isNullOrEmpty(phone)) {
                user = getUserByOpenId(openId, tenantId);
                if (user == null) {
                    throw new BusinessException("你的账号 [ " + phone + " ] 没有初始化，请与管理员联系。");
                }
            } else {
                user = baseMapper.selectById(personnel.getUserId());
                if (user == null) {
                    throw new BusinessException("你的账号 [ " + phone + " ] 没有初始化，请与管理员联系。");
                }
            }
        }

        // 找到微信用户与EBS用户的绑定信息
        UserWx userWx = userWxMapper.queryByUserIdAndTenantId(user.getId(), tenantId);
        if (userWx == null) {
            userWx = userWxMapper.queryByUserIdAndTenantId(openId, tenantId);
            if (userWx == null) {
                userWx = new UserWx();
                userWx.setUserId(user.getId());
                userWx.setTenantId(tenantId);
                userWx.setOpenId(openId);
            }
        }

        // 如果已有的openId所绑定的UserId不与当前手机号所绑定的用户一致，则需要检查用户类型，并进行合并处理
        if (!userWx.getUserId().equals(user.getId())) {
            User sourceUser = getById(userWx.getUserId());
            if (sourceUser != null) {
                if (sourceUser.getType() == UserTypeEnum.OTHER) {
                    logger.warn(String.format("正在合并账号至【%s】名下：openId=%s , sourceUserId=%s , targetUserId=%s", personnel.getName(), userWx.getOpenId(), sourceUser.getId(), user.getId()));
                    userWx.setUserId(user.getId());
                    sourceUser.setOpenId(null);
                    sourceUser.setUserName("`merge-" + System.currentTimeMillis() + "`" + user.getUserName());
                    sourceUser.setMergeStatus(1);
                    sourceUser.setMergeTime(new Date());
                    sourceUser.setMergeToId(user.getId());
                    sourceUser.updateById();
                    sourceUser.deleteById();
                }
            }
        } else {
            if (user.getType() == UserTypeEnum.OTHER) {
                user.setType(UserTypeEnum.USER);
                logger.warn("将账号 [ " + user.getUserName() + " : " + personnel.getName() + " ] 将升级为 [ " + UserTypeEnum.USER + " ] ");
            }
        }

        userWx.setOpenId(openId);
        userWx.insertOrUpdate();

        user.setCurrentTenantId(tenantId);
        user.setOpenId(openId);
        user.updateById();

        personnel.setUserId(user.getId());
        personnel.updateById();

    }

    @Override
    public void bindOpenId(String openId, String phone, String code) {
        if (!checkRegisterCode(phone, code)) {
            throw new BusinessException("验证码不正确，请重新输入。");
        }
        User user = getById(getUserExt(phone, null).getId());
        user.setOpenId(openId);
        user.updateById();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void bindOpenId(String openId, String phone, String code, String tenantId) {
        if (!checkRegisterCode(phone, code)) {
            throw new BusinessException("验证码不正确，请重新输入。");
        }
        User user = getById(getUserExt(phone, null).getId());
        user.setOpenId(openId);

        if (!StringUtils.isNullOrEmpty(tenantId)) {
            user.setCurrentTenantId(tenantId);
            UserWx userWx = userWxMapper.queryByOpenId(openId);
            if (userWx == null) {
                userWx = new UserWx();
                userWx.setOpenId(openId);
                userWx.setUserId(user.getId());
                userWx.setTenantId(tenantId);
                userWx.insert();
            }
        }
        user.updateById();
    }

    @Override
    public User createOpenIdUser(String openId, String tenantId) {
        LambdaQueryWrapper<UserWx> lq = Wrappers.lambdaQuery();
        lq.eq(UserWx::getOpenId, openId);
        UserWx userWx = userWxMapper.selectOne(lq);
        if (userWx == null) {
            LambdaQueryWrapper<User> userQuery = Wrappers.lambdaQuery();
            userQuery.or().eq(User::getOpenId, openId);
            userQuery.or().eq(User::getUserName, openId);
            User user = baseMapper.selectOne(userQuery);
            if (user == null) {
                logger.warn("正在创建新用户：" + openId);
                user = new User();
            }
            user.setType(UserTypeEnum.OTHER);
            user.setUserName(openId);
            user.setPassword(AESTool.Encrypt(openId));
            user.setIsDelete(false);
            user.setOpenId(openId);
            user.setCurrentTenantId(tenantId);
            user.insertOrUpdate();

            userWx.setTenantId(tenantId);
            userWx.setOpenId(openId);
            userWx.setUserId(user.getId());
            userWx.insert();
            return user;
        } else {
            return baseMapper.selectById(userWx.getUserId());
        }
    }

    @Override
    public UserExt getUserExt(String userId) {
        return userExtMapper.selectById(userId);
    }

    @Override
    public UserExt getUserExt(String phone, String openId) {
        LambdaQueryWrapper<UserExt> lq = Wrappers.lambdaQuery();
        if (StringUtils.isNullOrEmpty(openId)) {
            if (StringUtils.isNullOrEmpty(phone)) {
                throw new BusinessException("phone 和 openId 不能同时为空");
            } else {
                lq.eq(UserExt::getTel, phone);
            }
        } else {
            UserWx userWx = userWxMapper.queryByOpenId(openId);
            if (userWx == null) {
                //throw new BusinessException("当前微信号没有注册。");
                return null;
            } else {
                lq.eq(UserExt::getId, userWx.getUserId());
                lq.eq(UserExt::getOpenId, openId);
            }
        }

        if (userExtMapper.selectCount(lq) > 1) {
            throw new BusinessException("当前微信号或手机号存在多个账号绑定信息，请联系管理员处理。");
        }
        UserExt userExt = userExtMapper.selectOne(lq);
        if (userExt == null) {
            return null;
            //throw new BusinessException("您的手机号码没有找到。");
        }
        if (userExt.getIsDelete()) {
            throw new BusinessException("你的账号已被注销。");
        }
        if (userExt.getTenantIsDelete()) {
            throw new BusinessException("你所在的租户已被注销。");
        }
        if (userExt.getEntIsDelete()) {
            throw new BusinessException("你所在的企业已被注销。");
        }
        if (userExt.getPersonnelIsDelete()) {
            throw new BusinessException("你的个人信息已被注销。");
        }
        return userExt;
    }

    @Override
    public String getOpenId(String userId, String tenantId) {
        UserWx user = userWxMapper.queryByUserIdAndTenantId(userId, tenantId);
        if (user != null) {
            return user.getOpenId();
        } else {
            // 根据当前企业查所有租户和用户下有没有OpenId
            return userWxMapper.queryOpenIdByUserIdAndEntId(userId, SecurityUtils.getUserContent().getEntId());
            //return null;
        }
    }

    @Override
    public String getOpenIdByEnt(String userId, String entId) {
        LambdaQueryWrapper<UserWxExt> lq = Wrappers.lambdaQuery();
        lq.eq(UserWxExt::getUserId, userId);
        lq.eq(UserWxExt::getEntId, entId);
        lq.isNotNull(UserWxExt::getOpenId);
        Optional<UserWxExt> user = userWxExtMapper.selectList(lq).stream().findFirst();
        if (user.isPresent()) {
            return user.get().getOpenId();
        } else {
            return null;
        }
    }

    @Override
    public String getUserId(String openId) {
        LambdaQueryWrapper<UserWx> lq = Wrappers.lambdaQuery();
        lq.eq(UserWx::getOpenId, openId);
        List<UserWx> users = userWxMapper.selectList(lq);
        if (users == null) {
            return null;
        }
        if (users.size() <= 0) {
            return null;
        }
        return users.get(0).getUserId();
    }

    @Override
    public boolean bindStatus(String openId) {
        return getUserExt(null, openId) != null;
    }

    @Override
    public boolean bindStatus(String openId, String tenantId) {
        User user = getUserByOpenId(openId, tenantId);
        return user != null;
    }

    @Override
    public String login(String username, String password, String tenantId, String openId, WxMpUser wxMpUser) {

        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getUserName, username);
        User user = baseMapper.selectOne(wrapper);

        // 用户校验
        if (user == null) {
            throw new BusinessLoginUserNotFoundException(username);
        }
        if (user.getIsDelete()) {
            throw new BusinessLoginUserDeletedException(username);
        }
        if (!(AESTool.Decrypt(user.getPassword()).equals(password))) {
            System.out.println(AESTool.Decrypt(user.getPassword()));
            throw new BusinessLoginPasswordErrorException(username);
        }

        if (user.getType() == UserTypeEnum.USER || user.getType() == UserTypeEnum.TENANT_ADMIN || user.getType() == UserTypeEnum.OTHER
                || user.getType() == UserTypeEnum.DEVELOPER) {
            if (StringUtils.isNullOrEmpty(tenantId)) {
                if (StringUtils.isNullOrEmpty(user.getCurrentTenantId())) {
                    List<TenantInfo> tenants = getTenants(user.getId());
                    if (tenants.size() > 0) {
                        user.setCurrentTenantId(tenants.get(0).getTenantId());
                    }
                }
            } else {
                user.setCurrentTenantId(tenantId);
            }
            if (!StringUtils.isNullOrEmpty(openId)) {
                user.setOpenId(openId);
            }
        }
        user.setLastLoginTime(new Date());
        user.updateById();

        UserContext userInfo = markUserContext(user.getId(), user.getCurrentTenantId(), wxMpUser);

        List<AppUserRoleExt> roles = queryRoles(user.getId(), user.getCurrentTenantId());
        if (!StringUtils.isNullOrEmpty(roles)) {
            userInfo.setHomepageUrls(roles.stream().map(s -> s.getHomepageUrl()).distinct().collect(Collectors.toList()));
        }

        return createToken(userInfo);
    }

    @Transactional
    @Override
    public String loginByOpenId(String id, String tenantId, WxMpUser wxMpUser) {
        LambdaQueryWrapper<UserWx> lq = Wrappers.lambdaQuery();
        lq.eq(UserWx::getOpenId, id);
        lq.eq(UserWx::getTenantId, tenantId);
        List<UserWx> userWxList = userWxMapper.selectList(lq);
        if (userWxList.size() > 1) {
            logger.error("数据异常，同一租户（TenantID=" + tenantId + "）中存在多笔相同的（OpenID=" + id + "）记录");
            LambdaQueryWrapper<User> lqUser = Wrappers.lambdaQuery();
            lqUser.eq(User::getOpenId, id);
            lqUser.eq(User::getIsDelete, false);
            List<User> users = userMapper.selectList(lqUser);
            Optional<User> mainUser = users.stream().filter(s -> s.getType() != UserTypeEnum.OTHER).findFirst();
            if (users.size() > 1 && mainUser.isPresent()) {
                for (User u : users) {
                    if (u.getType() == UserTypeEnum.OTHER) {

                        // 合并处理
                        logger.warn("将账号 [ " + u.getUserName() + " ] 合并至 [ " + mainUser.get().getUserName() + " ] ");
                        u.setUserName("`merge-" + System.currentTimeMillis() + "`" + mainUser.get().getUserName());
                        u.setMergeStatus(1);
                        u.setOpenId(null);
                        u.setMergeToId(mainUser.get().getId());
                        u.setIsDelete(true);
                        u.updateById();

                        // 删除对应的 UserWx
                        LambdaQueryWrapper<UserWx> delWrapper = Wrappers.lambdaQuery();
                        delWrapper.eq(UserWx::getUserId, u.getId());
                        userWxMapper.delete(delWrapper);
                    }
                }
            }
        }

        // TODO: 2020/10/30 多此一举？
        //UserWx userWx = userWxMapper.selectList(lq).stream().findFirst().get();
        UserWx userWx = userWxList.size() <= 0 ? null : userWxList.stream().findFirst().get();
        if (userWx == null) {
            throw new BusinessException("openId无效或者你没有绑定到系统账号。");
        }
        if (wxMpUser != null) {
            userWx.setHeadImage(wxMpUser.getHeadImgUrl());
            userWx.setNickname(wxMpUser.getNickname());
            userWx.updateById();
        }

        User user = baseMapper.selectById(userWx.getUserId());
        return login(user.getUserName(), AESTool.Decrypt(user.getPassword()), tenantId, id, wxMpUser);
    }

    @Override
    public UserContext getUserContext() {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication auth = context.getAuthentication();
        if (auth == null) {
            throw new SystemException("当前没有设置用户上下文!");
        }

        String userId = ((DefaultClaims) auth.getPrincipal()).getSubject();
        String tenantId = ((DefaultClaims) auth.getPrincipal()).get("tenantId") == null ? null : ((DefaultClaims) auth.getPrincipal()).get("tenantId").toString();
        return getUserContext(userId, tenantId);
    }

    @Override
    public UserContext getUserContext(String userId, String tenantId) {
        return markUserContext(userId, tenantId, null);
    }

    @Override
    public UserContext getUserContext(String token) {
        Claims obj = JwtUtils.parseToken(token);
        if (obj == null) {
            throw new BusinessException("Token 无效。");
        }
        return getUserContext(obj.getSubject(), obj.get("tenantId").toString());
    }

    @Override
    public String createToken(UserContext userContext) {

        Map userMap = new HashMap();
        userMap.put("tenantId", userContext.getTenantId());

        String token = null;
        long exp = 0;
        if (userContext.getType() != UserTypeEnum.API_REQUEST) {
            exp = 60 * 12;
        }
        token = JwtUtils.createToken(userContext.getId(), userMap, exp);
        //redisService.set(user_key + userContext.getId(), userContext, exp * 60);
        return token;
    }

    @Override
    public String createOpenApiToken(String userId) {
        return createToken(markUserContext(userId, SecurityUtils.getTenantId(), null));
    }

    @Transactional
    @Override
    public String createOpenApiToken(String userName, String cnName) {
        LambdaQueryWrapper<User> lq = Wrappers.lambdaQuery();
        lq.eq(User::getUserName, userName);
        User user = baseMapper.selectOne(lq);
        if (user != null) {
            throw new BusinessException(String.format("%s 账号重复，请换一个名称。", userName));
        }

        user = new User();
        user.setType(UserTypeEnum.API_REQUEST);
        user.setUserName(userName);
        user.setPassword(AESTool.Encrypt(StringUtils.randomCode(10)));
        user.setCurrentTenantId(SecurityUtils.getTenantId());
        user.insert();

        Personnel personnel = new Personnel();
        personnel.setUserId(user.getId());
        personnel.setName(cnName);
        personnel.setSex(SexEnum.Notdefine.getCode());
        personnel.setIsDelete(false);
        personnel.insert();

        LambdaQueryWrapper<EntDept> lqDept = Wrappers.lambdaQuery();
        lqDept.eq(EntDept::getEntId, SecurityUtils.getUserContent().getEntId());
        lqDept.eq(EntDept::getIsDelete, false);
        List<EntDept> deptList = entDeptMapper.selectList(lqDept);
        EntDept dept = null;
        if (StringUtils.isNullOrEmpty(deptList)) {
            dept = new EntDept();
            dept.setEntId(SecurityUtils.getUserContent().getEntId());
            dept.setName("系统默认");
            dept.setSort(0);
            dept.setIsDelete(false);
            dept.insert();
        } else {
            dept = deptList.get(0);
        }

        EntPersonnel entPersonnel = new EntPersonnel();
        entPersonnel.setEntId(SecurityUtils.getUserContent().getEntId());
        entPersonnel.setPersonnelId(personnel.getId());
        entPersonnel.setDeptId(dept.getId());
        entPersonnel.setSort(9999);
        entPersonnel.setIsDelete(false);
        entPersonnel.insert();

        UserContext userInfo = markUserContext(user.getId(), SecurityUtils.getTenantId(), null);
        return createToken(userInfo);
    }

    private String queryTenantId(String userId) {
        LambdaQueryWrapper<AppUserRole> lq = Wrappers.lambdaQuery();
        lq.eq(AppUserRole::getUserId, userId);
        List<String> ids = appUserRoleMapper.selectList(lq).stream().map(s -> s.getTenantId()).distinct().collect(Collectors.toList());
        if (ids.size() <= 0) {
            return null;
        } else if (ids.size() > 1) {
            throw new BusinessException("当前用户找到了 " + ids.size() + " 个的租户，系统无法自动推断您需要以哪个租户身份进行操作。");
        } else {
            return ids.get(0);
        }
    }

    @Override
    public String queryByTenantAdminUserId(String tenantId) {
        LambdaQueryWrapper<TenantAdmin> lq = Wrappers.lambdaQuery();
        lq.eq(TenantAdmin::getTenantId, tenantId);
        Optional<TenantAdmin> admin = tenantAdminMapper.selectList(lq).stream().findFirst();
        if (admin.isPresent()) {
            return admin.get().getUserId();
        } else {
            return null;
        }
    }

    private List<UserTenantAdminExt> queryTenantAdminByUserId(String userId) {
        LambdaQueryWrapper<UserTenantAdminExt> lq = Wrappers.lambdaQuery();
        lq.eq(UserTenantAdminExt::getUserId, userId);
        return userTenantAdminExtMapper.selectList(lq);
    }

    @Override
    public UserContext markUserContext(String userId, String currTenantId, WxMpUser wxMpUser) {
        LambdaQueryWrapper<UserExt> lq = Wrappers.lambdaQuery();
        lq.eq(UserExt::getId, userId);
        if (!StringUtils.isNullOrEmpty(currTenantId)) {
            AppTenant tenant = appTenantService.queryById(currTenantId);
            if (tenant != null) {
                lq.eq(UserExt::getTenantId, currTenantId);
            }
        }
        UserExt userContext = userExtMapper.selectOne(lq);
        if (userContext == null) {
            if (appTenantService.queryById(currTenantId) == null) {
                String newTenantId = queryTenantId(userId);
                if (StringUtils.isNullOrEmpty(newTenantId)) {
                    throw new BusinessException("当前用户的租户信息没有找到。");
                }
            }
            return null;
        }
        //只有一般用户才需要验证租户信息
        if (userContext.getType() == UserTypeEnum.USER) {
            if (StringUtils.isNullOrEmpty(userContext.getTenantId())) {
                throw new BusinessNotSetTenantException(userContext.getUserName());
            }
            if (userContext.getTenantIsDelete()) {
                throw new BusinessTenantDeletedException(userContext.getAppName(), userContext.getEntName());
            }
            if (userContext.getTenantEndDate().before(new Date())) {
                throw new BusinessTenantExpirationException(userContext.getTenantEndDate());
            }
        }

        //租户验证
        List<TenantInfo> userTenantExts = getTenants(userContext.getId());
        if (StringUtils.isNullOrEmpty(userContext.getCurrentTenantId())) {
            if (userTenantExts.size() <= 0 && userContext.getType() == UserTypeEnum.USER) {
                throw new BusinessNotSetTenantException(userContext.getUserName());
            }
            if (userTenantExts.size() == 1) {
                if (userTenantExts.get(0).isDelete()) {
                    throw new BusinessTenantDeletedException(userTenantExts.get(0).getAppName(), userTenantExts.get(0).getEntName());
                }
                if (userTenantExts.get(0).getEndDate().before(new Date())) {
                    throw new BusinessTenantExpirationException(userTenantExts.get(0).getEndDate());
                }
            }
        }

        UserContext userInfo = new UserContext();
        org.springframework.beans.BeanUtils.copyProperties(userContext, userInfo);
        userInfo.setWxOpenId(userContext.getOpenId());
        userInfo.setTenants(userTenantExts);
        if (userInfo.getTenants().stream().filter(s -> s.isAdmin() == true && s.getTenantId().equals(userInfo.getTenantId())).findFirst().isPresent()) {
            if (userInfo.getType() == UserTypeEnum.USER) {
                userInfo.setType(UserTypeEnum.TENANT_ADMIN);
            }
        }

        UserWx userWx = userWxMapper.queryByOpenIdAndTeantId(userContext.getOpenId(), userContext.getTenantId());
        if (userWx != null) {
            userInfo.setWxOpenId(userWx.getOpenId());
            userInfo.setWxHeadImgUrl(userWx.getHeadImage());
            userInfo.setWxNickname(wxMpUser == null ? userWx.getNickname() : wxMpUser.getNickname());
            userInfo.setWxCountry(wxMpUser == null ? null : wxMpUser.getCountry());
            userInfo.setWxProvince(wxMpUser == null ? null : wxMpUser.getProvince());
            userInfo.setWxCity(wxMpUser == null ? null : wxMpUser.getCity());
            userInfo.setWxSexDesc(wxMpUser == null ? null : wxMpUser.getSexDesc());
            userInfo.setWxUnionId(wxMpUser == null ? null : wxMpUser.getUnionId());
        }

        return userInfo;
    }

    private List<TenantInfo> getTenants(String userId) {
        List<TenantInfo> result = new ArrayList<>();
        LambdaQueryWrapper<AppUserRole> queryUserRoles = Wrappers.lambdaQuery();
        queryUserRoles.eq(AppUserRole::getUserId, userId);
        List<AppUserRole> listRoles = appUserRoleMapper.selectList(queryUserRoles);
        List<String> tenantIds = listRoles.stream().map(s -> s.getTenantId()).distinct().collect(Collectors.toList());

//        if (tenantIds.size() <= 0) {
//            return result;
//        }

        LambdaQueryWrapper<TenantAdmin> queryTenantAdmin = Wrappers.lambdaQuery();
        queryTenantAdmin.eq(TenantAdmin::getUserId, userId);
        List<TenantAdmin> tenantAdmins = tenantAdminMapper.selectList(queryTenantAdmin);
        List<String> tenantAdminIds = tenantAdmins.stream().map(s -> s.getTenantId()).distinct().collect(Collectors.toList());
        tenantIds.addAll(tenantAdminIds);
        List<String> ids = tenantIds.stream().distinct().collect(Collectors.toList());

        if (StringUtils.isNullOrEmpty(ids)) {
            return result;
        }

        LambdaQueryWrapper<AppTenantExt> queryTenant = Wrappers.lambdaQuery();
        queryTenant.in(AppTenantExt::getId, ids);
        List<AppTenantExt> tenants = appTenantExtMapper.selectList(queryTenant);

        for (AppTenantExt tenant : tenants) {
            TenantInfo t = new TenantInfo();
            t.setTenantId(tenant.getId());
            t.setAppId(tenant.getAppId());
            t.setAppName(tenant.getAppName());
            t.setTenantName(tenant.getTenantName());
            t.setEntId(tenant.getEntId());
            t.setEntName(tenant.getEntName());
            t.setStartDate(tenant.getStartDate());
            t.setEndDate(tenant.getEndDate());
            t.setDelete(tenant.getIsDelete());
            if (tenantAdminIds.contains(tenant.getId())) {
                t.setAdmin(true);
            }
            result.add(t);
        }
//        result.addAll(queryTenantAdminByUserId(userId).stream().map(s->{
//            TenantInfo t = new TenantInfo();
//            t.setTenantId(s.getId());
//            t.setAppId(s.getAppId());
//            t.setAppName(s.getAppName());
//            t.setAdmin(true);
//            t.setStartDate(s.getStartDate());
//            t.setEndDate(s.getEndDate());
//            t.setTenantName(s.);
//        }))
//        for(UserTenantAdminExt item : queryTenantAdminByUserId(userId)){
//            result.add()
//        }
        return result;
    }

    @Override
    public void changeCurrentTenant(String newTenantId) {
        if (SecurityUtils.getUserContent().getTenants().equals(newTenantId)) {
            return;
        }

        if (!getTenants(SecurityUtils.getUserContent().getId()).stream().map(s -> s.getTenantId()).collect(Collectors.toList()).contains(newTenantId)) {
            throw new BusinessPermissionDeniedException("你没有目标租户权限。");
        }

        User user = baseMapper.selectById(SecurityUtils.getUserContent().getId());
        if (user == null) {
            throw new BusinessEntityNotFoundException(SecurityUtils.getUserContent().getId(), "用户");
        }

        user.setCurrentTenantId(newTenantId);
        user.updateById();
    }

    @Override
    public List<UserPowerExt> queryPowerByUserId(String id) {
        UserExt user = getUserExt(id);
        LambdaQueryWrapper<UserPowerExt> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(UserPowerExt::getUserId, id);
        wrapper.eq(UserPowerExt::getPowerIsDelete, false);
        wrapper.eq(UserPowerExt::getAppId, user.getAppId());
        wrapper.orderByAsc(UserPowerExt::getPowerSort);
        return userPowerExtMapper.selectList(wrapper);
    }

    @Override
    public List<PowerDto> queryPower() {
        if (SecurityUtils.getUserContent().getType() == UserTypeEnum.OTHER) {
            return new ArrayList<>();
        }
        if (SecurityUtils.getUserContent().getType() == UserTypeEnum.USER) {
            LambdaQueryWrapper<UserPowerExt> lq = Wrappers.lambdaQuery();
            lq.eq(UserPowerExt::getUserId, SecurityUtils.getUserContent().getId());
            lq.eq(UserPowerExt::getPowerIsDelete, false);
            lq.eq(UserPowerExt::getAppId, SecurityUtils.getUserContent().getAppId());
            lq.orderByAsc(UserPowerExt::getPowerSort);
            return userPowerExtMapper.selectList(lq).stream().map(s -> {
                PowerDto obj = new PowerDto();
                obj.setId(s.getId());
                obj.setPowerCode(s.getPowerCode());
                obj.setPowerIcon(s.getPowerIcon());
                obj.setPowerName(s.getPowerName());
                obj.setIsShowSubMenu(s.getIsShowSubMenu());
                obj.setParentId(s.getParentId());
                obj.setPowerSort(s.getPowerSort());
                obj.setPowerType(s.getPowerType());
                obj.setUrl(s.getUrl());
                return obj;
            }).distinct().collect(Collectors.toList());
        } else if (
                (SecurityUtils.getUserContent().getType() == UserTypeEnum.TENANT_ADMIN) ||
                        (SecurityUtils.getUserContent().getType() == UserTypeEnum.DEVELOPER && !StringUtils.isNullOrEmpty(SecurityUtils.getUserContent().getAppId()))) {
            LambdaQueryWrapper<AppPower> lq = Wrappers.lambdaQuery();
            lq.eq(AppPower::getAppId, SecurityUtils.getUserContent().getAppId());
            lq.eq(AppPower::getIsDelete, false);
            lq.orderByAsc(AppPower::getSort);
            return appPowerMapper.selectList(lq).stream().map(s -> {
                PowerDto obj = new PowerDto();
                obj.setId(s.getId());
                obj.setPowerCode(s.getCode());
                obj.setPowerIcon(s.getIcon());
                obj.setPowerName(s.getName());
                obj.setIsShowSubMenu(s.getIsShowSubMenu());
                obj.setParentId(s.getParentId());
                obj.setPowerSort(s.getSort());
                obj.setPowerType(s.getType());
                obj.setUrl(s.getUrl());
                return obj;
            }).distinct().collect(Collectors.toList());
        } else {
            return null;
        }
    }

    @Override
    public List<UserRoleExt> queryRoleByUserId(String id) {
        LambdaQueryWrapper<UserRoleExt> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(UserRoleExt::getUserId, id);
        wrapper.orderByAsc(UserRoleExt::getRoleSort);
        return userRoleExtMapper.selectList(wrapper);
    }

    @Override
    public List<UserAppExt> queryAppByUserId(String id) {
        QueryWrapper<UserAppExt> wrapper = Wrappers.query();
        wrapper.eq("user_id", id);
        wrapper.eq("app_is_delete", false);
        return userAppExtMapper.selectList(wrapper);
    }

    @Override
    public List<AppUserRoleExt> queryCurrRoles() {
        LambdaQueryWrapper<AppUserRoleExt> lq = Wrappers.lambdaQuery();
        lq.eq(AppUserRoleExt::getUserId, SecurityUtils.getUserContent().getId());
        lq.eq(AppUserRoleExt::getTenantId, SecurityUtils.getUserContent().getTenantId());
        return appUserRoleExtMapper.selectList(lq);
    }

    public List<AppUserRoleExt> queryRoles(String userId, String tenantId) {
        LambdaQueryWrapper<AppUserRoleExt> lq = Wrappers.lambdaQuery();
        lq.eq(AppUserRoleExt::getUserId, userId);
        lq.eq(AppUserRoleExt::getTenantId, tenantId);
        return appUserRoleExtMapper.selectList(lq);
    }

    @Override
    public boolean hasRole(String roleId) {
        return queryCurrRoles().stream().filter(s -> s.getId().equals(roleId)).count() >= 1;
    }

    @Override
    public User queryByUserName(String userName) {
        LambdaQueryWrapper<User> lq = Wrappers.lambdaQuery();
        lq.eq(User::getUserName, userName);
        lq.or().eq(User::getOpenId, userName);
        return userMapper.selectOne(lq);
    }

    @Override
    public UserWx queryUserWx(String tenantId, String openId) {
        return userWxMapper.queryByOpenIdAndTeantId(openId, tenantId);
    }

    @Override
    public UserWx queryUserWxByUser(String tenantId, String userId) {
        return userWxMapper.queryByUserIdAndTenantId(userId, tenantId);
    }

    @Override
    public String queryUserHandImage(String userId, String tenantId) {
        if (StringUtils.isNullOrEmpty(userId)) {
            return null;
        }

        LambdaQueryWrapper<UserWx> lq = Wrappers.lambdaQuery();
        lq.eq(UserWx::getUserId, userId);
        lq.eq(UserWx::getTenantId, tenantId);
        Optional<UserWx> obj = userWxMapper.selectList(lq).stream().findFirst();
        if (obj.isPresent()) {
            if (!StringUtils.isNullOrEmpty(obj.get().getHeadImage())) {
                if (!obj.get().getHeadImage().contains("http")) {
                    return OSSUtil.getObjectUrl(obj.get().getHeadImage()).toString();
                } else {
                    return obj.get().getHeadImage();
                }
            }
        }
        return null;
    }

    @Override
    public User queryByPersonnelId(String id) {
        LambdaQueryWrapper<Personnel> lq = Wrappers.lambdaQuery();
        lq.eq(Personnel::getId, id);
        lq.eq(Personnel::getIsDelete, false);
        Optional<Personnel> personnel = personnelMapper.selectList(lq).stream().findFirst();
        if (personnel.isPresent()) {
            return baseMapper.selectById(personnel.get().getUserId());
        } else {
            return null;
        }
    }

    @Override
    public List<UserDto> queryApiRequestUser(String entId) {
        LambdaQueryWrapper<UserExt> lq = Wrappers.lambdaQuery();
        if (!StringUtils.isNullOrEmpty(entId)) {
            lq.eq(UserExt::getEntId, entId);
        }
        lq.eq(UserExt::getType, UserTypeEnum.API_REQUEST);
        lq.eq(UserExt::getIsDelete, false);
        lq.eq(UserExt::getAppIsDelete, false);
        lq.eq(UserExt::getEntIsDelete, false);
        lq.eq(UserExt::getTenantIsDelete, false);
        return userExtMapper.selectList(lq).stream().map(s -> {
            UserDto user = new UserDto();
            user.setId(s.getId());
            user.setUserName(s.getUserName());
            user.setPersonnelId(s.getPersonnelId());
            user.setPersonnelName(s.getPersonnelName());
            user.setDeptId(s.getDeptId());
            user.setDeptName(s.getDeptName());
            user.setEntId(s.getEntId());
            user.setEntName(s.getEntName());
            user.setTenantId(s.getTenantId());
            user.setTenantName(s.getTenantName());
            user.setSecretKey(AESTool.Decrypt(s.getPassword()));
            return user;
        }).collect(Collectors.toList());
    }

//    @Override
//    public UserContext getUserContext(String token) {
//        return redisService.get(user_key + token);
//    }
}